/*
 * Copyright (c) 2010, Tangedyn <tangedyn at gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the Mew Developers nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package yawning.mew;

import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.math.stat.descriptive.SummaryStatistics;

public class Report {
	private String mLog;
	private int mIterations = 1;
	private Map<IStat,Result> mPerformances = new HashMap<IStat,Result>();
	private Map<IStat,Double> mPerformancesSingular = new HashMap<IStat,Double>();

	public interface IStat {
	}

	public enum Stat implements IStat {
		DAMAGE_DEALT_PER_SECOND,
		DAMAGE_TAKEN_PER_SECOND,
		DAMAGE_PREVENTED_PER_SECOND,
		HEALING_DONE_PER_SECOND,
		HEALING_TAKEN_PER_SECOND,
		THREAT_PER_SECOND,
		TIME_TO_LIVE,
		MITIGATION_VALUE,
	}

	public class Result extends SummaryStatistics {
		private static final long serialVersionUID = 1L;

		public double getStandardError() {
			return getStandardDeviation() / Math.sqrt(getN());
		}
	}

	public void setLog(String log) {
		mLog = log;
	}

	public String getLog() {
		return mLog;
	}

	public void setResult(IStat stat, double p) {
		if (mPerformances.containsKey(stat)) throw new RuntimeException();	// Debugging

		mPerformancesSingular.put(stat, p);
	}

	public Result getResult(IStat stat) {
		Result s = mPerformances.get(stat);

		// Formulation doesn't actually call accumulate(), so carve out SummaryStatistics entries here.
		if (s == null) {
			s = new Result();
			s.addValue(mPerformancesSingular.get(stat));
		}

		return s;
	}

	public int getIterations() {
		return Math.max(mIterations - 1, 1);
	}

	public Report accumulate(Report r) {
		for (IStat v : r.mPerformancesSingular.keySet()) {
			double value = r.mPerformancesSingular.get(v);
			Result s = mPerformances.get(v);

			// This routine explicitly does not support combining two accumulated reports.
			if (r.mPerformances.containsKey(v)) throw new RuntimeException();	// Debugging

			if (s == null) {
				// There's a first time for everything.
				s = new Result();
				s.addValue(value);
				if (mPerformancesSingular.containsKey(v)) {
					s.addValue(mPerformancesSingular.get(v));
				}
				mPerformances.put(v, s);
			} else {
				s.addValue(value);
			}
		}

		if (r.mLog != null) mLog = r.mLog;

		mIterations += r.mIterations;

		return this;
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		for (IStat s : mPerformances.keySet()) {
			Result stat = getResult(s);
			sb.append(s).append(": ").append(stat.getMean());
			sb.append(" ±").append(stat.getStandardError() * 1.96);
			sb.append(" StdDev: ").append(stat.getStandardDeviation());
		}
        return sb.toString();
	}

	public static String round(double x) {
		// TODO: Hard-coded to 5 for now, need to get it from.. somewhere?
		DecimalFormat df = new DecimalFormat("#.#####");
		return df.format(x);
	}
}
